#include <sys/socket.h>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>


using namespace std;

int main(){

    //1、创建监听的文件描述符
    int lfd=socket(AF_INET,SOCK_STREAM,0);
    if(lfd==-1){
        perror("socket error");
        exit(-1);
    }

    //2、绑定端口和ip
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(9990);
    saddr.sin_addr.s_addr=INADDR_ANY;

    int ret=bind(lfd,(struct sockaddr*) &saddr,sizeof(saddr));
    if(ret==-1){
        perror("bind error");
        exit(-1);
    }
    //端口复用
    int optval=1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));


    //3、监听
    ret =listen(lfd,10);
    if(ret==-1){
        perror("listen error");
        exit(-1);
    }

    //创建一个epoll实例
    int epfd=epoll_create(100);

    struct epoll_event evt;
    evt.events=EPOLLIN;
    evt.data.fd=lfd;

    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&evt);

    struct epoll_event epevs[1024];

    cout<<"server listening on port "<<saddr.sin_port<<"..."<<endl;
    while(1){
        int rets=epoll_wait(epfd,epevs,1024,-1);
        if(rets==-1){
            perror("epoll_wait error");
            exit(-1);
        }

        for(int i=0;i<rets;++i){
            int curfd=epevs[i].data.fd;
            if(curfd==lfd){
                //监听的文件描述符有数据到达，有客户端连接
                struct sockaddr_in clientAddr;
                socklen_t len=sizeof(clientAddr);

                int cfd=accept(lfd,(struct sockaddr*) &clientAddr,&len);

                //设置cfd为非阻塞
                int flag=fcntl(cfd,F_GETFL);
                flag|=O_NONBLOCK;
                fcntl(cfd,F_SETFL,flag);

                //加入到epfd中,设置为ET模式
                evt.events=EPOLLIN|EPOLLET;
                evt.data.fd=cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&evt);

                //输出客户端信息
                char clientIP[16];
                inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
                //网络字节序转换为主机字节序
                unsigned short clientPort=ntohs(clientAddr.sin_port);
                cout<<"client addr="<<clientIP<<"    client port="<<clientPort<<endl;

            }else{
                if(epevs[i].events&EPOLLOUT){
                    continue;
                }

                //有数据到达，需要通信
                char buf[1024];
                int len=0;
                while((len=recv(curfd,buf,sizeof(buf),0))>0){
                    cout<<"read data="<<buf<<endl;
                    const char* response="hello, from server";
                    send(curfd,response,strlen(response),0);
                }
                if(len==0){
                    perror("client clase");
                    exit(-1);
                }else if(len==-1){
                    if(errno==EAGAIN){
                        cout<<"data over"<<endl;
                    }else{
                        perror("read");
                        exit(-1);
                    }
                }
            }
        }
    }
    close(lfd);
    close(epfd);
    return 0;
}
